<?php
/**
 * @author 595949289@qq.com
 * @date 2024/12/30 9:38
 */

namespace CuiFox\yii\filters;

use Yii;
use yii\web\User;
use yii\base\Action;
use yii\di\Instance;
use yii\base\ActionFilter;
use yii\web\ForbiddenHttpException;

class AccessControl extends ActionFilter
{
    /**
     * @var User $user
     */
    public $user = 'user';

    /**
     * @throws \yii\base\InvalidConfigException
     * @throws \Exception
     */
    public function init()
    {
        parent::init();

        if ($this->user !== false) {
            $this->user = Instance::ensure($this->user, User::class);
        }
    }

    /**
     * @param Action $action
     * @return bool
     * @throws ForbiddenHttpException
     */
    public function beforeAction($action)
    {
        $actionId = $action->getUniqueId();
        if ($this->can('/' . $actionId, Yii::$app->getRequest()->get())) {
            return true;
        }
        $this->denyAccess();
    }

    /**
     * @param $route
     * @param array $params
     * @return bool
     */
    public function can($route, $params = [])
    {
        $user = $this->user;

        if ($user->can($route, $params)) {
            return true;
        }

        while (($pos = strrpos($route, '/')) > 0) {
            $route = substr($route, 0, $pos);
            if ($user->can($route . '/*', $params)) {
                return true;
            }
        }

        return $user->can('/*', $params);
    }

    /**
     * @throws ForbiddenHttpException
     */
    protected function denyAccess()
    {
        $user = $this->user;
        if ($user !== false && $user->getIsGuest()) {
            $user->loginRequired();
        } else {
            throw new ForbiddenHttpException(Yii::t('yii', 'You are not allowed to perform this action.'));
        }
    }

    /**
     * @param Action $action
     * @return bool
     */
    protected function isActive($action)
    {
        $uniqueId = $action->getUniqueId();
        if ($uniqueId === Yii::$app->getErrorHandler()->errorAction) {
            return false;
        }

        $user = $this->user;
        if ($user->getIsGuest()) {
            $loginUrl = null;
            if (is_array($user->loginUrl) && isset($user->loginUrl[0])) {
                $loginUrl = $user->loginUrl[0];
            } else {
                if (is_string($user->loginUrl)) {
                    $loginUrl = $user->loginUrl;
                }
            }
            if (!is_null($loginUrl) && trim($loginUrl, '/') === $uniqueId) {
                return false;
            }
        }

        return parent::isActive($action);
    }
}